package com.qdlc.p2p.biz.service.impl;

import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import com.google.common.collect.Lists;
import com.google.gson.Gson;
import com.qd.p2p.adapter.Response;
import com.qd.p2p.adapter.enums.ResponseStatus;
import com.qd.p2p.sina.pay.enums.IdentityType;
import com.qdlc.p2p.biz.processor.event.RepayCollectNoticeEvent;
import com.qdlc.p2p.biz.service.AbstractCollectService;
import com.qdlc.p2p.biz.service.AccountService;
import com.qdlc.p2p.biz.service.BorrowRepaymentService;
import com.qdlc.p2p.biz.service.CollectPayService;
import com.qdlc.p2p.biz.service.RPCRequestService;
import com.qdlc.p2p.common.constant.BorrowRepaymentStatus;
import com.qdlc.p2p.common.constant.BorrowRepaymentType;
import com.qdlc.p2p.common.constant.BorrowStatus;
import com.qdlc.p2p.common.constant.GeneralAccountType;
import com.qdlc.p2p.common.constant.Global;
import com.qdlc.p2p.common.constant.LogTplNid;
import com.qdlc.p2p.common.constant.NotifyUrl;
import com.qdlc.p2p.common.constant.Summary;
import com.qdlc.p2p.common.constant.TradeCode;
import com.qdlc.p2p.common.constant.TradeStatus;
import com.qdlc.p2p.common.core.ReturnResult;
import com.qdlc.p2p.common.core.queue.QueueHandlers;
import com.qdlc.p2p.common.exception.BorrowException;
import com.qdlc.p2p.common.util.BigDecimalUtil;
import com.qdlc.p2p.common.util.DateUtil;
import com.qdlc.p2p.dal.common.SystemConfigHelper;
import com.qdlc.p2p.dal.common.SystemConfigHelper.Nid;
import com.qdlc.p2p.dal.dao.AccountDao;
import com.qdlc.p2p.dal.dao.BorrowDao;
import com.qdlc.p2p.dal.dao.BorrowRepaymentDao;
import com.qdlc.p2p.dal.dto.Borrow;
import com.qdlc.p2p.dal.dto.BorrowRepayment;
import com.qdlc.p2p.dal.dto.TppTradeLog;
import com.qdlc.p2p.dal.util.UserUtils;

@Service("borrowRepayCollectService")
public class BorrowRepayCollectServiceImpl extends AbstractCollectService {

    private final static String BUSINESS_NAME = "还款代收本息-";

    /**
     * 还款计划
     */
    private ThreadLocal<BorrowRepayment> borrowRepaymentLocal = new ThreadLocal<BorrowRepayment>();

    @Resource
    private BorrowDao borrowDao;

    @Resource
    private BorrowRepaymentDao borrowRepaymentDao;

    @Resource
    private AccountDao accountDao;

    @Resource
    private BorrowRepaymentService borrowRepaymentService;

    @Resource
    private AccountService accountService;

    @Resource
    @Qualifier("borrowRepayUpRateCollectService")
    private CollectPayService borrowRepayUpRateCollectService;

    @Resource
    @Qualifier("bfRPCRequestService")
    private RPCRequestService<Response, TppTradeLog> rpcRequestService;

    @Override
    public void analyzePars(Object... pars) {
        if (pars.length != 1) {
            throw new IllegalArgumentException("参数个数正确!");
        }
        try {
            this.borrowRepaymentLocal.set((BorrowRepayment) pars[0]); // 还款计划
        } catch (Exception ex) {
            throw new IllegalArgumentException("参数类型正确!");
        }
    }

    @Override
    public TppTradeLog getTppTradeLog() {
        BorrowRepayment borrowRepayment = borrowRepaymentLocal.get();
        Borrow borrow = borrowRepayment.getBorrow();

        double transAmount = BigDecimalUtil.add(borrowRepayment.getCapital(),
                borrowRepayment.getInterest()); // 还款金额(本金 + 利息)
        int period = borrowRepayment.getPeriod(); // 期数

        // TppTradeLog生成
        Map<String, Object> memoMap = new HashMap<String, Object>();
        memoMap.put("transAmount", transAmount); // 还款金额
        memoMap.put("period", period); // 期数

        // 代收借款人还款金额订单
        TppTradeLog tppTradeLog = new TppTradeLog();
        tppTradeLog.setTppUserCustId(UserUtils.getUserTppCustId(borrow.getUser().getUserId())); // 借款人tppId
        tppTradeLog.setToTppUserCustId("0");
        tppTradeLog.setIdentityType(IdentityType.UID.toString());
        tppTradeLog.setMoney(transAmount); // 还款金额
        tppTradeLog.setBorrowId(borrow.getId()); // 标ID
        tppTradeLog.setBorrowRepaymentId(borrowRepayment.getId()); // 还款ID
        tppTradeLog.setMemo((new Gson()).toJson(memoMap));
        tppTradeLog.setTradeType(TradeCode.C1004.toString());
        tppTradeLog.setServiceType(Summary.REPAY.getSummaryType());
        return tppTradeLog;
    }

    @Override
    public List<TppTradeLog> createOrders(Object... pars) {
        List<TppTradeLog> tppTradeLogs = Lists.newArrayList();

        // 主订单
        TppTradeLog mainTradeLog = super.createOrder(pars);
        tppTradeLogs.add(mainTradeLog);

        // 创建加息订单
        TppTradeLog upRateTppTradeLog = borrowRepayUpRateCollectService.createOrder(borrowRepaymentLocal.get());
        // 保存还款加息订单
        if (upRateTppTradeLog != null) {
            tppTradeLogs.add(upRateTppTradeLog);
        }
        return tppTradeLogs;
    }

    @Override
    public void aftCreateOrder(TppTradeLog tppTradeLog) {
        BorrowRepayment borrowRepayment = borrowRepaymentLocal.get();
        long borrowId = borrowRepayment.getBorrow().getId();
        Borrow borrow = borrowDao.find(borrowId);

        // 把未还款状态更新为 => 还款中
        boolean result = borrowRepaymentDao.updateStatus(BorrowRepaymentStatus.STATUS_REPAY_ING,
                BorrowRepaymentStatus.STATUS_WAIT_REPAY, borrowRepaymentLocal.get().getId());
        if (result == false) {
            logger.error(MessageFormat.format("该标状态不正确!borrowId = {0}", tppTradeLog.getBorrowId()));
            throw new BorrowException("系统繁忙,请稍后重试!", BorrowException.TYPE_JSON);
        }
        // 冻结还款金额
        double transAmount = BigDecimalUtil.add(borrowRepayment.getCapital(), borrowRepayment.getInterest()); // 还款金额(本金 + 利息)
        Global.setTransfer(LogTplNid.LOG_TPL_KEY, LogTplNid.REPAY_FREEZE);
        Global.setTransfer("web_url", WEB_URL);
        Global.setTransfer("borrow", borrow);
        Global.setTransfer("money", transAmount);
        accountService.freeze(borrow.getUser().getUserId(), borrow.getUser().getUserId(), transAmount);
    }

    @Override
    public ReturnResult requestOrder(TppTradeLog tppTradeLog) {
        // 主订单
        if (TradeCode.C1004.toString().equals(tppTradeLog.getTradeType())) {
            Response response = rpcRequestService.repayRequest(tppTradeLog);
            // 未知状态通知 交由消息队列处理
            if (response == null || response.getStatus() == ResponseStatus.UN_KNOW) {
                notice(tppTradeLog, TradeStatus.TRADE_STATUS_UN_KNOW);
                return new ReturnResult(true, "系统比较繁忙!请查看还款结果!");
            }

            if (response.getStatus() == ResponseStatus.SUCCESS) {
                tppTradeLog.setRemark(getBusinessName().concat("提交成功"));
                tppTradeService.update(tppTradeLog);
                notice(tppTradeLog, TradeStatus.TRADE_STATUS_FINISHED);
                requestOrderSuccess(tppTradeLog); // 请求成功处理
            } else if (response.getStatus() == ResponseStatus.FAILURE) {
                String message = response == null ? "RPC调用失败" : MessageFormat.format("{0}代收失败! ResCode:{1} ResMessage:{2}",
                        getBusinessName(), response.getResponseCode(),
                        response.getResponseMsg());
                String responseMessage = response == null ? "RPC调用失败" : response.getResponseMsg();
                logger.info(message);

                // 置为废单
                tppTradeLog.setTradeStatus(TradeStatus.TRADE_STATUS_INVALID.getStatus());
                tppTradeLog.setRemark(getBusinessName().concat("提交失败-" + responseMessage));
                tppTradeService.update(tppTradeLog);

                requestOrderFail(tppTradeLog); // 请求失败处理
                return new ReturnResult(true, message);
            }
            return ReturnResult.SUCCESS;
            // 加息订单
        } else if (TradeCode.C1005.toString().equals(tppTradeLog.getTradeType())) {
            return borrowRepayUpRateCollectService.requestOrder(tppTradeLog);
        }
        return null;
    }

    @Override
    public void requestOrderFail(TppTradeLog tppTradeLog) {
        Borrow borrow = borrowDao.find(tppTradeLog.getBorrowId());

        // 解冻还款金额
        Global.setTransfer(LogTplNid.LOG_TPL_KEY, LogTplNid.REPAY_UN_FREEZE);
        Global.setTransfer("web_url", WEB_URL);
        Global.setTransfer("borrow", borrow);
        Global.setTransfer("money", tppTradeLog.getMoney());
        long userId = borrow.getUser().getUserId();
        accountService.unFreeze(userId, userId, tppTradeLog.getMoney());

        // 更新为未还款状态
        boolean result = borrowRepaymentDao.updateStatus(BorrowRepaymentStatus.STATUS_WAIT_REPAY,
                BorrowRepaymentStatus.STATUS_REPAY_ING,
                tppTradeLog.getBorrowRepaymentId());
        if (result == false) {
            logger.error(MessageFormat.format("该标状态不正确!borrowId = {0}", tppTradeLog.getBorrowId()));
        }
    }

    /**
     * 还款代收 本息 完成
     */
    @Override
    public void tradeFinish(TppTradeLog tppTradeLog, String depositStatus, double tradeAmount) {
        super.tradeFinishForStatus(tppTradeLog, depositStatus, tradeAmount);

        logger.debug(getBusinessName().concat("交易完成 " + depositStatus));
        // 还款信息修改
        long borrowRepaymentId = tppTradeLog.getBorrowRepaymentId();
        BorrowRepayment borrowRepayment = borrowRepaymentService.find(borrowRepaymentId);
        Borrow borrow = borrowRepayment.getBorrow();
        // 非还款中 说明可能已被还款掉了 PS 理论上应该不会出现这情况
        if (borrowRepayment.getStatus() != BorrowRepaymentStatus.STATUS_REPAY_ING) {
            logger.debug(MessageFormat.format("请勿重复还款!状态:{0}", borrowRepayment.getStatus()));
            throw new BorrowException("请勿重复还款!");
        }

        // 从还款人用户冻结账户中扣除还款本金
        double capital = borrowRepayment.getCapital();
        if (capital > 0) {
            Global.setTransfer("web_url", SystemConfigHelper.getValue(Nid.WEB_URL));
            Global.setTransfer(LogTplNid.LOG_TPL_KEY, LogTplNid.REPAY_CAPITAL);
            Global.setTransfer("money", capital);
            Global.setTransfer("borrow", borrow);
            accountService.deductNoUseMoneyAndTotal(borrow.getUser().getUserId(), GeneralAccountType.INVESTOR, capital);
        }

        // 从还款人用户冻结账户中扣除还款利息
        double interest = borrowRepayment.getInterest();
        if (interest > 0) {
            Global.setTransfer("web_url", SystemConfigHelper.getValue(Nid.WEB_URL));
            Global.setTransfer(LogTplNid.LOG_TPL_KEY, LogTplNid.REPAY_INTEREST);
            Global.setTransfer("money", interest);
            Global.setTransfer("borrow", borrow);
            accountService.deductNoUseMoneyAndTotal(borrow.getUser().getUserId(), GeneralAccountType.INVESTOR, interest);
        }
        // TODO 逾期利息

        borrowRepayment.setRepaymentYesTime(DateUtil.getNow());// 还款时间
        borrowRepayment.setWebStatus(1);// 网站还款
        borrowRepayment.setStatus(BorrowRepaymentStatus.STATUS_YES_REPAY); // 设置为已还款
        borrowRepayment.setType(BorrowRepaymentType.REPAYMENT_TYPE_NORAML); // 正常还款
        borrowRepayment.setRealRepayer(borrow.getUser());  // 实际还款者
        borrowRepayment.setRepaymentYesAccount(tradeAmount); // 已还款金额
        // 更新还款信息
        borrowRepaymentDao.updateBorrowRepaymentByStatus(borrowRepayment);

        // 部分还款还是全部还款
        int status = borrow.getTimeLimit() == (borrowRepayment.getPeriod() + 1) ? BorrowStatus.STATUS_REPAYMENT_DONE : BorrowStatus.STATUS_REPAYMENT_DOING;
        borrow.setStatus(status);
        borrow.setRepaymentYesAccount(BigDecimalUtil.add(borrow.getRepaymentYesAccount(), tradeAmount));  // 已还金额
        borrow.setRepaymentYesInterest(BigDecimalUtil.add(borrow.getRepaymentYesInterest(), borrowRepayment.getInterest()));  // 已还利息
        borrowDao.update(borrow);

        // TODO 向借款人发送还款成功通知
    }

    /**
     * 还款代收 本息 成功
     */
    @Override
    public void tradeSuccess(TppTradeLog tppTradeLog, String depositStatus) {
        super.tradeSuccessForStatus(tppTradeLog, depositStatus);
    }

    /**
     * 还款代收 本息 失败
     */
    @Override
    public void tradeFail(TppTradeLog tppTradeLog, String depositStatus) {
        super.tradeFailForStatus(tppTradeLog, depositStatus);
        requestOrderFail(tppTradeLog);
    }

    @Override
    public String getCallBackUrl() {
        return WEB_URL.concat(NotifyUrl.REPAY_NOTIFY_COLLECT_URL).concat(".html");
    }

    @Override
    public String getBusinessName() {
        return BUSINESS_NAME;
    }

    /**
     * 发送还款通知 交由队列来执行
     */
    private void notice(TppTradeLog tppTradeLog, TradeStatus tradeStatus) {
        RepayCollectNoticeEvent event = new RepayCollectNoticeEvent();
        event.setClientIp(Global.getIP());
        event.setTradeStatus(tradeStatus);
        event.setTppTradeLog(tppTradeLog);
        event.setOutOrderNo(tppTradeLog.getOrderNo());
        event.setDepositAmount(tppTradeLog.getMoney());
        QueueHandlers.put(event);
    }
}
